#pragma once

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#include<strings.h>
#include<string>
#include"Log.hpp"

void NonBlock(int fd)
{
    int flags = fcntl(fd, F_GETFL, 0);//获取文件描述符的状态
    if(flags < 0)
        return;
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);//设置非阻塞
}
enum ReturnCode{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    CONNECT_ERR,
    ACCEPT_ERR = -1
};

#define BACKLOG 1024//监听队列的大小

class Socket{//基类(接口类)
    public:
        virtual ~Socket() = default;
        virtual void Create_Socket() = 0;
        virtual void ReuseAddr() = 0;//设置套接字选项---开启地址端⼝重⽤
        virtual void Listen() = 0;
        virtual void Bind_Socket(int port,const std::string &ip = "0.0.0.0") = 0;
        virtual void Connect(const std::string &ip, uint16_t port) = 0;//连接到服务器接口
        virtual int Accept(struct sockaddr *client_addr,int *acerrno) = 0;
        virtual ssize_t Recv(void *buf, size_t len, int flag = 0) = 0;
        virtual ssize_t Send(const void *buf, size_t len, int flag = 0) = 0;
        virtual int Fd() = 0;
        virtual void Close() = 0;
        void Build_Server_Socket(int port,const std::string &ip = "0.0.0.0")//端口号由上层传递
        {
            Create_Socket();//创建套接字
            ReuseAddr();//设置套接字选项---开启地址端重用 ,//必须在 bind() 之前调用 setsockopt()
            Bind_Socket(port,ip);//绑定端口
            Listen();//监听

        }
        void Build_Client_Socket(int port,const std::string &ip = "0.0.0.0")//客户端套接字
        {
            Create_Socket();
            Connect(ip,port);
        }
};

class TcpSocket : public Socket{//TCP
    private:
        int _sockfd;
    public:
        TcpSocket(int fd = -1):_sockfd(fd){}
        void SetNonBlock()//设置套接字为非阻塞
        {
            NonBlock(_sockfd);
        }
        virtual void Create_Socket() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if(_sockfd < 0){
                LOG_ERR("create socket failed");
                exit(SOCKET_ERR);
            }
            LOG_INF("create socket success %d", _sockfd);
        }
        virtual void ReuseAddr() override//设置套接字选项---开启地址端⼝重⽤;保证我们的服务器，异常断开之后，可以立即重启，不会有bind问题
        {
            int opt = 1;
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));//开启地址端重用
            setsockopt(_sockfd, SOL_SOCKET, SO_REUSEPORT, (void *)&opt, sizeof(opt));//开启端口重用
        }
        virtual void Listen() override
        {
            if(_sockfd < 0){
                LOG_ERR("socket not create");
                exit(SOCKET_ERR);
            }    
            int ret = listen(_sockfd, BACKLOG);
            if(ret < 0){
                LOG_ERR("listen failed");
                exit(SOCKET_ERR);
            }
            LOG_INF("listen success %d", _sockfd);
        }
        virtual void Bind_Socket(int port,const std::string &ip = "0.0.0.0") override
        {
            if(_sockfd < 0){
                LOG_ERR("socket not create");
                exit(BIND_ERR);
            }
            struct sockaddr_in server_addr;
            bzero(&server_addr, sizeof(server_addr));
            server_addr.sin_family = AF_INET;
            server_addr.sin_addr.s_addr = inet_addr(ip.c_str());//接受任何ip地址
            server_addr.sin_port = htons(port);
            int ret = bind(_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
            if(ret < 0){
                LOG_ERR("bind failed");
                exit(BIND_ERR);
            }
            LOG_INF("bind success %d", _sockfd);
        }
        virtual void Connect(const std::string &ip, uint16_t port) override//客户端连接到服务器接口
        {
            struct sockaddr_in server_addr;
            bzero(&server_addr, sizeof(server_addr));
            server_addr.sin_family = AF_INET;//IPv4
            server_addr.sin_port = htons(port);
            server_addr.sin_addr.s_addr = inet_addr(ip.c_str());
            int ret = connect(_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
            if(ret < 0){
                LOG_ERR("connect failed");
                exit(CONNECT_ERR);
            }
            LOG_INF("connect to %s:%d success %d", ip.c_str(), port, _sockfd);
        }
        virtual int Accept(struct sockaddr *client_addr,int *acerrno) override//服务器获取客户端连接
        {
            if(client_addr == nullptr)
                return ACCEPT_ERR;
            socklen_t client_len = sizeof(struct sockaddr_in);
            int connect_sockfd = ::accept(_sockfd,client_addr, &client_len);
            *acerrno = errno;//获取错误码
            if(connect_sockfd < 0){
                LOG_ERR("accept failed");
                return ACCEPT_ERR;
            }
            LOG_INF("accept success %d", connect_sockfd);
            return connect_sockfd;
        
        }
        virtual ssize_t Recv(void *buf, size_t len, int flag = 0) override
        {
            ssize_t n = recv(_sockfd, buf, len, flag);
            if(n<=0){
                //EAGAIN 当前socket的接收缓冲区中没有数据了，在⾮阻塞的情况下才会有这个错误
                //EWOULDBLOCK 当前socket的接收缓冲区中没有数据了，在阻塞的情况下才会有这个错误
                //EINTR 系统调用被信号中断，需要重新发起系统调用
                if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)//非阻塞模式下，读操作没有数据可读，返回0
                    return 0;
                else if(n<0){
                    printf("errno: %d\n", errno);
                    LOG_ERR("recv failed");
                    return -1;
                }
            }
            return n;
        }
        virtual ssize_t Send(const void *buf, size_t len, int flag = 0) override
        {
            ssize_t n = send(_sockfd, buf, len, flag);
            if(n < 0){
                if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
                    return 0;
                else{
                    LOG_ERR("send failed");
                    return -1;
                }
            }
            LOG_DBG("send %ld bytes", n);
            return n;
        }
        virtual int Fd() override
        {
            return _sockfd;
        }
        virtual void Close() override
        {
            if(_sockfd <0)
                return;
            close(_sockfd);
        }
};



